/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package proy1sistdistribuidos.modelo.seguridad;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Clase que encripta y desencripta cadenas utilizando el algoritmo Blowfish
 * @author Carlos Diaz Maya, Juan Felipe Garcia
 */
public class EncriptadorDesencriptadorDES implements EncriptadorDesencriptador {
    private String metodo = "DES";
    //private Integer tamanhoLlave = 56;  //Nota: Solo soporta 128 bits en los laboratorios de la sala
    private KeyGenerator genLlaves;
    private static EncriptadorDesencriptadorDES instancia;

    private EncriptadorDesencriptadorDES() throws Exception {
         // Se crea el generador de llaves
        try {
            genLlaves = KeyGenerator.getInstance(metodo);
            //genLlaves.init(tamanhoLlave);
        } catch(Exception f) {
            throw new Exception ("ERROR GRAVE: No se pudo construir el generador de llaves", f);
        }
    }

    public static EncriptadorDesencriptador obtenerSolitario() throws Exception {
         if(instancia == null){
            instancia = new EncriptadorDesencriptadorDES();
        }
        return instancia;
    }

   public void escribirLogInfo(String mensaje) {
       Logger.getLogger(this.getClass().getName()).log(Level.INFO, mensaje);
    }

    /**
     * Fuente del metodo: http://java.sun.com/developer/technicalArticles/Security/AES/AES_v1.html
     * Turns array of bytes into string
     *
     * @param buf	Array of bytes to convert to hex string
     * @return	Generated hex string
     */
     public static String asHex (byte buf[]) {
      StringBuffer strbuf = new StringBuffer(buf.length * 2);
      int i;

      for (i = 0; i < buf.length; i++) {
       if (((int) buf[i] & 0xff) < 0x10)
	    strbuf.append("0");

       strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
      }

      return strbuf.toString();
     }

     /**
      * Fuente del metodo http://stackoverflow.com/questions/140131/convert-a-string-representation-of-a-hex-dump-to-a-byte-array-using-java
      * Convierte una representacion en cadena hexadecimal de un arreglo de bytes a arreglo de bytes
      * @param hexString la representacion del arreglo de bytes en cadena hexadecimal
      * @return El arreglo de bytes correspondientes a la cadena hexadecimal
      */
     public static byte[] asByteArray (String hexString){
        int longitud = hexString.length();
        byte[] retorno = new byte[longitud/2];
        for (int i=0; i<longitud; i+=2) {
            retorno[i/2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i+1), 16));
        }
        return retorno;

     }


    @Override
    public SecretKey construirLlave() {
        SecretKey llaveAES = genLlaves.generateKey();
        return llaveAES;
    }

    @Override
    public String encriptarMensaje(String mensaje, SecretKey llave) throws Exception {
       if (mensaje == null || llave == null) {
           return null;
       } else {
           //Se obtiene la especificacion de la llave
           SecretKeySpec especificacionLlave = new SecretKeySpec(llave.getEncoded(), metodo);

           //Se obtiene el cifrador
           Cipher cifrador = Cipher.getInstance(metodo);
           //Se inicializa en modo de encripcion
           cifrador.init(Cipher.ENCRYPT_MODE, especificacionLlave);

           this.escribirLogInfo("**********INICIO ENCRIPCION MENSAJE**********");
           this.escribirLogInfo("Mensaje de entrada:" + mensaje);
           byte[] mensajeSalidaBytes = cifrador.doFinal(mensaje.getBytes());
           String mensajeSalida = asHex(mensajeSalidaBytes);
           this.escribirLogInfo("Mensaje de salida:" + mensajeSalida);
           this.escribirLogInfo("**********FIN ENCRIPCION MENSAJE**********");
           return mensajeSalida;
       }
    }

    @Override
    public String desencriptarMensaje(String mensaje, SecretKey llave) throws Exception {
       if (mensaje == null || llave == null) {
           return null;
       } else {
           //Se obtiene la especificacion de la llave
           SecretKeySpec especificacionLlave = new SecretKeySpec(llave.getEncoded(), metodo);

           //Se obtiene el cifrador
           Cipher cifrador = Cipher.getInstance(metodo);
           //Se inicializa en modo de desencripcion
           cifrador.init(Cipher.DECRYPT_MODE, especificacionLlave);

           this.escribirLogInfo("**********INICIO DESENCRIPCION MENSAJE**********");
           this.escribirLogInfo("Mensaje de entrada:" + mensaje);
           byte[] mensajeSalidaBytes = cifrador.doFinal(asByteArray(mensaje));
           String mensajeSalida = new String(mensajeSalidaBytes);
           this.escribirLogInfo("Mensaje de salida:" + mensajeSalida);
           this.escribirLogInfo("**********FIN DESENCRIPCION MENSAJE**********");
           return mensajeSalida;
       }
    }
}
